home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 076-100 / 079 / kill / kill.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  15KB  |  508 lines

  1. /*
  2.  *  KILL version 1.01
  3.  *  by George Musser Jr.
  4.  *
  5.  *  11 Jan 87 - initial version
  6.  *  24 Jan 87 - send ACTION_DIE packet to ConsoleTask for Wbench processes
  7.  *
  8.  *  Removes a task and as much of its hinterland as possible.  We can
  9.  *  close windows and unload process code, but screens and other,
  10.  *  unidentified structures elude us.  We can remove all but 40 or so
  11.  *  bytes of an empty CLI.
  12.  *
  13.  *  Syntax: KILL <task name>
  14.  *          KILL <task pointer>
  15.  *
  16.  *  I certainly don't claim that this program is the proper way to deal with
  17.  *  the Amiga system.  Hopefully, DOS 1.3 will come with a legitimate way to
  18.  *  kill runaway and undesirable processes.
  19.  *
  20.  *  Things to try in the next version:
  21.  *    - improve the killing of Workbench-spawned processes (somehow get
  22.  *      at the stdin window via process->pr_ConsoleTask);
  23.  *    - try to kill a non-process;
  24.  *    - experiment with trackdisk.device (killing the device stops
  25.  *      the drive from clicking; perhaps a SleepAmiga utility could
  26.  *      kill trackdisk, await a keypress, and Mount the drive);
  27.  *    - how does the system associate the CON devices with windows?
  28.  *      maybe we can use this to kill devices and Workbench processes
  29.  *      more throrughly.
  30.  *
  31.  *  Geo's net addresses:
  32.  *    CIS 76566,3714           (log on at least biweekly)
  33.  *    Plink OHS152             (log on at least monthly)
  34.  *    ST801115@BROWNVM.BITNET  (log on at least weekly)
  35.  */
  36.  
  37. /***** Includes ***********************************************************/
  38.  
  39. #include <exec/types.h>
  40. #include <exec/nodes.h>
  41. #include <exec/memory.h>
  42. #include <exec/tasks.h>
  43. #include <exec/ports.h>
  44. #include <exec/execbase.h>
  45. #include <libraries/dos.h>
  46. #include <libraries/dosextens.h>
  47. #include <intuition/intuitionbase.h>
  48. #include <intuition/intuition.h>
  49. #include <workbench/startup.h>
  50. #include <stdio.h>
  51.  
  52. /***** Defines ************************************************************/
  53.  
  54. #define LIMIT1_OF_PATIENCE  1  /* Time to wait for death by CLOSEWINDOW */
  55. #define LIMIT2_OF_PATIENCE  1  /* Time to wait for death by CTRL-C */
  56. #define LIMIT3_OF_PATIENCE  1  /* Time to wait for console to die  */
  57. #define MAX_WINDOWS        25  /* Maximum number of windows we can close */
  58.  
  59. /***** Global variables ***************************************************/
  60.  
  61. struct IntuitionBase *IntuitionBase;
  62. extern struct DosLibrary *DOSBase;
  63.  
  64. /***** main() *************************************************************/
  65.  
  66. main (argc,argv)
  67. char **argv;
  68. int argc;
  69. {
  70.    void exit();
  71.    struct IntuitionBase *OpenLibrary(char *,long);
  72.    int Kill(char *);
  73.    void CloseLibrary();
  74.  
  75.    int rc;
  76.  
  77.    if (argc < 2)                       /* Not enough arguments, so quit */
  78.       exit (RETURN_ERROR);
  79.  
  80.    IntuitionBase = OpenLibrary("intuition.library",LIBRARY_VERSION);
  81.  
  82.    rc = Kill(*++argv);
  83.  
  84.    if (IntuitionBase)
  85.       CloseLibrary (IntuitionBase);
  86.  
  87.    exit (rc);
  88. }
  89.  
  90. /***** Kill() *************************************************************
  91.  *
  92.  *  Kill() is a function so that we can experiment with recursion, which
  93.  *  may be needed to kill the console task.
  94.  *
  95.  */
  96.  
  97. int Kill (name)
  98. char *name;
  99. {
  100.  
  101.    /***** Functions *******************************************************/
  102.  
  103.    int stch_i();
  104.    void Forbid();
  105.    struct Task *FindTask(char *);
  106.    void Permit();
  107.    void fprintf();
  108.    ULONG LockIBase(long);
  109.    void UnlockIBase(long);
  110.    APTR AllocMem(long,long);
  111.    void PutMsg();
  112.    void Delay(long);
  113.    void CloseLibrary();
  114.    void FreeMem();
  115.    void Signal(struct Task *,long);
  116.    void UnLock(BPTR);
  117.    void UnLoadSeg(BPTR);
  118.    void Close(BPTR);
  119.    struct Message *GetMsg(struct MsgPort *);
  120.    struct MsgPort *FindPort(char *);
  121.    void ReplyMsg();
  122.    void RemTask(struct Task *);
  123.    void ClearMenuStrip(struct Window *);
  124.    void CloseWindow(struct Window *);
  125.  
  126.    /***** Local variables *************************************************/
  127.  
  128.    ULONG ilock;           /* Lock on IntuitionBase, so rug stays under us */
  129.  
  130.    struct Task *task;       /* This will point to the task we must remove */
  131.  
  132.    struct RootNode *rootnode;   /* Central AmigaDOS structure */
  133.    APTR *TaskTable;             /* List of processes */
  134.  
  135.    struct Screen *screen;                   /* Points to a screen */
  136.    struct Window *window;                   /* ...and to a window */
  137.    int nWindow;                             /* Number of task's windows */
  138.    struct Window *WindowList[MAX_WINDOWS];  /* Windows opened by task */
  139.  
  140.    unsigned long i;               /* Loop counter */
  141.    struct IntuiMessage *message;  /* Our forged IntuiMessage */
  142.  
  143.    struct Process *process;           /* Oops, task is part of a process */
  144.    struct CommandLineInterface *cli;  /* What's more, it's a CLI process */
  145.    BPTR *SegArray;                    /* A process has a list of segments */
  146.    unsigned long nSeg;                /* Number of process segments */
  147.  
  148.    struct MsgPort *myport;          /* Reply port for packet */
  149.    struct StandardPacket *packet;   /* Message to shut down console */
  150.    struct WBStartup *startup;       /* Forged message to Workbench */
  151.  
  152.    struct DOSLibrary *dos;  /* So that we can close DOS library */
  153.  
  154.    /***** Here we go ******************************************************/
  155.  
  156.    Forbid();
  157.    if (stch_i(name,&task) < 3)                 /* If user gave a string, */
  158.       if ((task = FindTask(name)) == NULL) {   /* find the corresponding */
  159.          Permit();                             /* task.                  */
  160.          fprintf (stderr,"Can't find `%s'.\n",name);
  161.          return (RETURN_ERROR);
  162.          }
  163.  
  164.    if (task == FindTask(NULL)) {              /* No suicide pills here */
  165.       Permit();
  166.       fprintf (stderr,"Can't kill self.\n");
  167.       return (RETURN_ERROR);
  168.       }
  169.  
  170. #ifdef DEBUG
  171.    Permit();
  172.    printf ("Killing task %lx\n",task);
  173.    Forbid();
  174. #endif DEBUG
  175.  
  176.    /* Find windows associated with the task */
  177.  
  178.    nWindow = 0;
  179.  
  180.    if (IntuitionBase) {
  181.          
  182.       ilock = LockIBase(0);
  183.  
  184.       /* Loop through screens */
  185.  
  186.       screen = IntuitionBase->FirstScreen;
  187.       while (screen && nWindow < MAX_WINDOWS) {
  188.  
  189.          /* Loop through windows */
  190.  
  191.          window = screen->FirstWindow;
  192.          while (window && nWindow < MAX_WINDOWS) {
  193.  
  194.             /*  If this window has an IDCMP, then it points back to
  195.              *  the task.  Check if this task is the one we want.
  196.              */
  197.  
  198.             if (window->UserPort->mp_SigTask == task)
  199.                WindowList[nWindow++] = window;
  200.  
  201.             window = window->NextWindow;
  202.  
  203.             }  /* windows */
  204.  
  205.          screen = screen->NextScreen;
  206.          }  /* screens */
  207.  
  208.       UnlockIBase (ilock);
  209.  
  210.       }  /* IntuitionBase */
  211.  
  212.    /*  First we'll give the task a chance to die quietly.  Fake an Intuition
  213.     *  CLOSEWINDOW message and send it to an IDCMP equipped to handle such
  214.     *  an event.  Give the task a second to die and check if it's still
  215.     *  around.  If so...
  216.     */
  217.  
  218.    if (nWindow) {
  219.  
  220.       message = (struct IntuiMessage *)AllocMem(sizeof(struct IntuiMessage),
  221.                                                 MEMF_PUBLIC);
  222.  
  223.       i = nWindow;
  224.       while (i)
  225.          if (WindowList[--i]->IDCMPFlags & CLOSEWINDOW) {
  226.  
  227.             message->Class = CLOSEWINDOW;
  228.             message->IDCMPWindow = WindowList[i];
  229.             message->ExecMessage.mn_ReplyPort = WindowList[i]->WindowPort;
  230.             message->ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
  231.             message->ExecMessage.mn_Length = sizeof(struct IntuiMessage) -
  232.                                              sizeof(struct Message);
  233.  
  234.             Permit();
  235.             PutMsg (WindowList[i]->UserPort,message);
  236.  
  237.             Delay (LIMIT1_OF_PATIENCE * TICKS_PER_SECOND);
  238.             Forbid();
  239.  
  240.             FreeMem (message,sizeof(struct IntuiMessage));
  241.  
  242.             if (FindTask(name) != task) {
  243.                Permit();
  244.                return (RETURN_OK);
  245.                }
  246.             }
  247.  
  248.       }
  249.  
  250.    if (task->tc_Node.ln_Type == NT_PROCESS) {
  251.  
  252.       /*  Plan B.  Try breaking the task with the CTRL-C/CTRL-D combination.
  253.        *  Wait a little while and check whether the task is still there.
  254.        *  Even if the plan doesn't kill the task, it'll break out of the
  255.        *  current CLI command and thereby allow us to close the CLI window.
  256.        */
  257.  
  258.       Permit();
  259.  
  260. #ifdef DEBUG
  261.    printf ("Sending break signals...");
  262. #endif DEBUG
  263.  
  264.       Signal (task,SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
  265.       Delay (LIMIT2_OF_PATIENCE * TICKS_PER_SECOND);
  266.       Forbid();
  267.  
  268.       if (FindTask(name) != task) {
  269.          Permit();
  270. #ifdef DEBUG
  271.          puts ("");
  272. #endif DEBUG
  273.          return (RETURN_OK);
  274.          }
  275.  
  276. #ifdef DEBUG
  277.       Permit();
  278.       printf ("done.\n");
  279.       Forbid();
  280. #endif DEBUG
  281.  
  282.       /*  Well, looks as though we'll have to pry it loose by hand. */
  283.  
  284.       process = (struct Process *)task;
  285.       SegArray = (BPTR *)BADDR(process->pr_SegList);
  286.  
  287. #ifdef DEBUG
  288.       Permit();
  289.       printf ("Killing process %lx\n",&process->pr_MsgPort);
  290.       Forbid();
  291. #endif DEBUG
  292.  
  293.       /*  Delete the process from AmigaDOS's list.  First, dig up the master
  294.        *  list of processes.  Second, scan through the list for our task and
  295.        *  reset its pointer to NULL.
  296.        */
  297.  
  298.       rootnode = (struct RootNode *)DOSBase->dl_Root;
  299.       TaskTable = (APTR *)BADDR(rootnode->rn_TaskArray);
  300.  
  301.       for (i = 1; i <= (unsigned long)TaskTable[0]; i++)
  302.          if (TaskTable[i] == (APTR)&process->pr_MsgPort) {
  303.             TaskTable[i] = NULL;
  304.             break;
  305.             }
  306.  
  307.       if (process->pr_TaskNum) {
  308.  
  309.          /*  It's not a child of Workbench, so we will have to clean up.
  310.           *  First, unlock the current directory.  Second, check whether
  311.           *  the CLI is in the midst of executing a command.  If so,
  312.           *  unload the command's code.  If not, close the CLI's I/O.
  313.           *  Finally, unload the CLI's code.
  314.           */
  315.  
  316.          UnLock (process->pr_CurrentDir);
  317.  
  318.          cli = (struct CommandLineInterface *)BADDR(process->pr_CLI);
  319.  
  320.          if (cli->cli_Module)
  321.             UnLoadSeg (cli->cli_Module);
  322.          else {
  323.  
  324.             /*  Close CLI I/O files, if the CLI isn't executing a command.
  325.              *  I tried to close cli_StandardInput, et. al., and to check
  326.              *  whether any other processes used these file handles as their
  327.              *  CLI I/O.  These additional measures had no effect.  Under
  328.              *  the present code, if you kill a CLI from which a background
  329.              *  CLI had spawned, AmigaDOS will wait until the background
  330.              *  process is done before closing the CLI window.
  331.              *
  332.              *  Closing files when the CLI was in the midst of executing a
  333.              *  command crashed the system.  The CTRL-C/CTRL-D combination
  334.              *  above helps.
  335.              */
  336.  
  337.             if (cli->cli_Interactive) {
  338.                if (process->pr_CIS) Close (process->pr_CIS);
  339.                if (process->pr_COS) Close (process->pr_COS);
  340.                }
  341.  
  342.             }  /* else cli->cli_Module */
  343.  
  344.          /* Remove process code and table thereof */
  345.     
  346.          nSeg = SegArray[0];
  347.          while (--nSeg > 2)
  348.             UnLoadSeg (SegArray[nSeg]);
  349.          FreeMem (SegArray,SegArray[0] * sizeof(BPTR));
  350.  
  351.          }  /* if (process->pr_TaskNum) */
  352.  
  353.       else {
  354.  
  355.          /*  Send a death message to the ConsoleTask associated with this
  356.           *  process.  I'm not sure if this actually does anything, but
  357.           *  but it's part of an attempt to close the standard I/O window
  358.           *  opened by the startup code.
  359.           */
  360.  
  361.          if (process->pr_ConsoleTask) {
  362.             myport = &((struct Process *)FindPort(NULL))->pr_MsgPort;
  363.  
  364.             packet = (struct StandardPacket *)
  365.                      AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC);
  366.  
  367.             packet->sp_Msg.mn_Node.ln_Type = NT_MESSAGE;
  368.             packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
  369.             packet->sp_Msg.mn_ReplyPort = myport;
  370.             packet->sp_Msg.mn_Length = sizeof(struct DosPacket);
  371.             packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
  372.             packet->sp_Pkt.dp_Port = packet->sp_Msg.mn_ReplyPort;
  373.             packet->sp_Pkt.dp_Type = ACTION_DIE;
  374.  
  375.             Permit();
  376.  
  377. #ifdef DEBUG
  378.             printf ("Sending message %lx to %lx...",
  379.                     packet,process->pr_ConsoleTask);
  380. #endif DEBUG
  381.  
  382.             PutMsg (process->pr_ConsoleTask,packet);
  383.             Delay (LIMIT3_OF_PATIENCE * TICKS_PER_SECOND);
  384.             if (GetMsg(myport) == &packet->sp_Msg)
  385.                FreeMem (packet,sizeof(struct StandardPacket));
  386.  
  387. #ifdef DEBUG
  388.             printf ("done.\n");
  389. #endif DEBUG
  390.  
  391.             Forbid();
  392.  
  393.             }
  394.  
  395.          /*  We're killing a child of Workbench.  Let Workbench mop up the
  396.           *  mess.  Fake a message to Workbench in reply to its startup
  397.           *  message.
  398.           */
  399.  
  400.          startup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),
  401.                                                 MEMF_PUBLIC);
  402.  
  403.          startup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
  404.          startup->sm_Message.mn_ReplyPort = FindPort("Workbench");
  405.          startup->sm_Message.mn_Length = sizeof(struct WBStartup) -
  406.                                          sizeof(struct Message);
  407.          startup->sm_Process = &process->pr_MsgPort;
  408.          startup->sm_Segment = SegArray[3];
  409.          startup->sm_NumArgs = 0;
  410.          startup->sm_ToolWindow = NULL;
  411.          startup->sm_ArgList = NULL;
  412.          Permit();
  413.  
  414. #ifdef DEBUG
  415.          printf ("Sending message %lx...",startup);
  416. #endif DEBUG
  417.  
  418.          ReplyMsg (startup);
  419.  
  420. #ifdef DEBUG
  421.          printf ("done.\n");
  422. #endif DEBUG
  423.  
  424.          Forbid();
  425.  
  426.          }  /* else (process->pr_TaskNum) */
  427.  
  428.       /* Close libraries opened by process */
  429.  
  430. #ifdef DEBUG
  431.       Permit();
  432.       printf ("Closing DOS...");
  433.       Forbid();
  434. #endif DEBUG
  435.  
  436.       dos = (struct DOSLibrary *)DOSBase;
  437.       CloseLibrary (dos);
  438.  
  439. #ifdef DEBUG
  440.       Permit();
  441.       printf ("done.\n");
  442.       Forbid();
  443. #endif DEBUG
  444.  
  445.       /*  At last we can kill the task.  Besides pulling the task from
  446.        *  Exec's sight, RemTask() seems to free the Task structure, the task
  447.        *  stack, and memory that is specified in the task's tc_MemEntry list
  448.        *  (verified empirically).  For processes, tc_MemEntry covers the
  449.        *  Process structure and the process stack.
  450.        */
  451.  
  452. #ifdef DEBUG
  453.       Permit();
  454.       printf ("Killing task...");
  455.       Forbid();
  456. #endif DEBUG
  457.  
  458.       RemTask (task);
  459.  
  460. #ifdef DEBUG
  461.       Permit();
  462.       printf ("done.\n");
  463.       Forbid();
  464. #endif DEBUG
  465.  
  466.       }  /* process */
  467.  
  468.    else {
  469.  
  470.       /*  I haven't experimented with non-processes yet.  For now, all
  471.        *  we do is remove the task from Exec's list.
  472.        */
  473.  
  474.       RemTask (task);
  475.       FreeMem (task,sizeof(struct Task));
  476.  
  477.       }
  478.  
  479.    /* Close windows */
  480.  
  481. #ifdef DEBUG
  482.    Permit();
  483.    printf ("Closing windows...");
  484.    Forbid();
  485. #endif
  486.  
  487.    while (nWindow) {
  488.  
  489.       if (WindowList[--nWindow]->MenuStrip)
  490.          ClearMenuStrip (WindowList[nWindow]);
  491.  
  492.       CloseWindow (WindowList[nWindow]);
  493.  
  494.       }
  495.  
  496. #ifdef DEBUG
  497.    Permit();
  498.    printf ("done.\n");
  499.    Forbid();
  500. #endif DEBUG
  501.  
  502.    Permit();
  503.  
  504.    return (RETURN_OK);
  505.  
  506. }
  507.  
  508.